using System;
using BepuUtilities;
#if MYCODE
using BepuUtilities.Vectors;
#else
using System.Numerics;
#endif
namespace DemoRenderer
{
    /// <summary>
    /// 简单的偏航/俯仰锁定相机。
    /// </summary>
    public class Camera
    {
        /// <summary>
        /// 获取或设置相机的位置。
        /// </summary>
        public Vector3 Position { get; set; }

        float yaw;
        /// <summary>
        /// 获取或设置相机的偏转,作为从-Pi到Pi的值。值为0时,向前与-z对齐。在PI/2处,向前与+x对齐。换句话说,较高的值向右转。
        /// </summary>
        public float Yaw
        {
            get { return yaw; }
            set
            {
                var revolution = (value + Math.PI) / (2 * Math.PI);
                revolution -= Math.Floor(revolution);
                yaw = (float)(revolution * (Math.PI * 2) - Math.PI);
            }
        }
        float pitch;
        /// <summary>
        /// 获取或设置相机的间距,固定为从-MaximumPitch到MaximumPitch的值。较高的值向下看,较低的值向上看。
        /// </summary>
        public float Pitch
        {
            get { return pitch; }
            set { pitch = Math.Clamp(value, -maximumPitch, maximumPitch); }
        }

        float maximumPitch;
        /// <summary>
        /// 获取或设置相机的最大间距,值从0到PI/2。
        /// </summary>
        public float MaximumPitch
        {
            get { return maximumPitch; }
            set { maximumPitch = (float)Math.Clamp(value, 0, Math.PI / 2); }
        }

        /// <summary>
        /// 获取或设置相机的纵横比。
        /// </summary>
        public float AspectRatio { get; set; }

        /// <summary>
        /// 获取或设置相机的垂直视场。
        /// </summary>
        public float FieldOfView { get; set; }

        /// <summary>
        /// 获取或设置相机的近平面。
        /// </summary>
        public float NearClip { get; set; }

        /// <summary>
        /// 获取或设置相机的远平面。
        /// </summary>
        public float FarClip { get; set; }

        // 所有这些都可能要快得多,但是每帧浪费几千个周期并不是问题。
        /// <summary>
        /// 获取相机的方向四元数。
        /// </summary>
        public Quaternion OrientationQuaternion
        {
            get
            {
                QuaternionEx.CreateFromYawPitchRoll(-yaw, -pitch, 0, out var orientationQuaternion);
                return orientationQuaternion;
            }
        }

        /// <summary>
        /// 获取相机的方向变换。
        /// </summary>
        public Matrix Orientation
        {
            get
            {
                return Matrix.CreateFromQuaternion(OrientationQuaternion);
            }
        }

        /// <summary>
        /// 获取相机的正确方向。等效于按方向变换(1,0,0)。
        /// </summary>
        public Vector3 Right
        {
            get
            {
                var orientation = OrientationQuaternion;
                QuaternionEx.TransformUnitX(orientation, out var right);
                return right;
            }
        }
        /// <summary>
        /// 获取相机的左方向。等效于按方向变换(-1,0,0)。
        /// </summary>
        public Vector3 Left
        {
            get
            {
                return -Right;
            }
        }
        /// <summary>
        /// 获取相机的向上方向。等效于按方向变换(0,1,0)。
        /// </summary>
        public Vector3 Up
        {
            get
            {
                var orientation = OrientationQuaternion;
                QuaternionEx.TransformUnitY(orientation, out var up);
                return up;
            }
        }
        /// <summary>
        /// 获取相机的向下方向。等效于按方向变换(0,-1,0)。
        /// </summary>
        public Vector3 Down
        {
            get
            {
                return -Up;
            }
        }
        /// <summary>
        /// 获取相机的向后方向。等效于按方向变换(0,0,1)。
        /// </summary>
        public Vector3 Backward
        {
            get
            {
                var orientation = OrientationQuaternion;
                QuaternionEx.TransformUnitZ(orientation, out var backward);
                return backward;
            }
        }
        /// <summary>
        /// 获取相机的前进方向。等效于按方向变换(0,0,-1)。
        /// </summary>
        public Vector3 Forward
        {
            get
            {
                return -Backward;
            }
        }

        /// <summary>
        /// 获取摄影机的世界变换。
        /// </summary>
        public Matrix World
        {
            get
            {
                var world = Orientation;
                world.Translation = Position;
                return world;
            }
        }

        /// <summary>
        /// 获取相机的视图变换。
        /// </summary>
        public Matrix View
        {
            get
            {
                return Matrix.Invert(World);
            }
        }

        /// <summary>
        /// 使用反转深度获取相机的投影变换。
        /// </summary>
        public Matrix Projection
        {
            get
            {
                // 注意翻转的近/远！反转深度。更精确的分配。我们不太可能在演示中利用这一点,但嘿,这是免费的房地产。
                return Matrix.CreatePerspectiveFieldOfView(FieldOfView, AspectRatio, FarClip, NearClip);
            }
        }

        /// <summary>
        /// 获取相机的组合视图*投影。
        /// </summary>
        public Matrix ViewProjection
        {
            get
            {
                return View * Projection;
            }
        }

        /// <summary>
        /// 创建新相机。
        /// </summary>
        /// <param name="aspectRatio">相机投影的纵横比。</param>
        /// <param name="fieldOfView">相机投影的垂直视野。</param>
        /// <param name="near Clip">相机投影的近剪裁平面。</param>
        /// <param name="farClip">相机投影的远剪裁平面。</param>
        /// <param name="maxumPitch">相机可以向上或向下查看的最大角度。</param>
        public Camera(float aspectRatio, float fieldOfView, float nearClip, float farClip, float maximumPitch = System.MathF.PI * 0.499f)
        {
            AspectRatio = aspectRatio;
            FieldOfView = fieldOfView;
            MaximumPitch = maximumPitch;
            NearClip = nearClip;
            FarClip = farClip;
        }

        /// <summary>
        /// 获取给定鼠标状态的光线方向。
        /// </summary>
        /// <param name="MouseLocked">鼠标当前是否被锁定。如果锁定,将使用与屏幕中心对应的射线。</param>
        /// <param name="NormalizedMousePosition">相对于窗口边界规范化为[0,1]的鼠标位置。</param>
        /// <return>指向鼠标方向的世界空格线方向。</return>
        public Vector3 GetRayDirection(bool mouseLocked, in Vector2 normalizedMousePosition)
        {
            // 光线方向取决于相机以及相机是否已锁定。
            if (mouseLocked)
            {
                return Forward;
            }
            var unitPlaneHalfHeight = System.MathF.Tan(FieldOfView * 0.5f);
            var unitPlaneHalfWidth = unitPlaneHalfHeight * AspectRatio;
            var localRayDirection = new Vector3(
                new Vector2(unitPlaneHalfWidth, unitPlaneHalfHeight) * 2 * new Vector2(normalizedMousePosition.X - 0.5f, 0.5f - normalizedMousePosition.Y), -1);
            QuaternionEx.TransformWithoutOverlap(localRayDirection, OrientationQuaternion, out var rayDirection);
            return rayDirection;
        }


    }
}
